package br.com.srv.componentes.relatorio.velocidademedia.service;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import br.com.srv.commons.service.impl.BasicServiceImpl;
import br.com.srv.componentes.cerca.util.PontosTODataComparator;
import br.com.srv.componentes.jdbcdao.DadosLidoJdbcDAO;
import br.com.srv.componentes.relatorio.velocidademedia.util.GeometriaAnalitica;
import br.com.srv.componentes.rota.service.RotaService;
import br.com.srv.model.PontoTO;
import br.com.srv.model.RotaTO;
import br.com.srv.model.VeiculoTO;
import br.com.srv.util.DateHelper;
import br.com.srv.web.relatorio.VelocidadeMediaTO;

public class VelocidadeMediaServiceImpl
	extends BasicServiceImpl implements VelocidadeMediaService {
	
	RotaService rotaService;
	DadosLidoJdbcDAO dadosLidoJdbcDAO;
	
	GeometriaAnalitica geometriaAnalitica = new GeometriaAnalitica();

	public List<Map<String, Object>> consultaVelocidadeMedia(
			VelocidadeMediaTO velocidadeMediaTO) throws Exception {		
		
		List<Map<String, Object>> resultado = new ArrayList<Map<String, Object>>();
		
		//retorna a RotaTO preenchida.
		RotaTO rotaTO = (RotaTO)getBasicDAO().findById(
				RotaTO.class, new Integer(velocidadeMediaTO.getRotaId()));

		//recupera os dados do ponto inicial
		PontoTO pontoInicial = (PontoTO)getBasicDAO().findById(
				PontoTO.class, new Integer(velocidadeMediaTO.getPontoFisInicial()));

		//recupera os dados do ponto final
		PontoTO pontoFinal = (PontoTO)getBasicDAO().findById(
				PontoTO.class, new Integer(velocidadeMediaTO.getPontoFisFinal()));
		
		//recupera a lista de veiculos com os pontos atuais
		List<VeiculoTO> veiculos = getRotaService().buscarVeiculosDaRota(rotaTO.getId());		
		
		for (VeiculoTO veiculoTO : veiculos) {
			
			//Guarda o resultado
			List<VelocidadeMediaTO> velocidadesMedia = new ArrayList<VelocidadeMediaTO>();
			
			VelocidadeMediaTO velocidadeMediaTO2 = new VelocidadeMediaTO();
			velocidadeMediaTO2.setDescricaoVeiculo(veiculoTO.getDescricao());
			velocidadeMediaTO2.getPeriodoTO().setDataInicial("-");
			velocidadeMediaTO2.getPeriodoTO().setDataFinal("-");				
			velocidadeMediaTO2.setVelocidadeMediaVeiculo("0");			
			
			//passar data para formato aceito no DB.
			String dataInicial = DateHelper.parseStringDateToStringDateJdbc(
					velocidadeMediaTO.getPeriodoTO().getDataInicial());
			String dataFinal = DateHelper.parseStringDateToStringDateJdbc(
					velocidadeMediaTO.getPeriodoTO().getDataFinal());
			
			//Pegar pontos do veiculo
			List<Map<String,String>> pontos = getDadosLidoJdbcDAO().consultaPontosByVeiculo(
					veiculoTO.getRealId().toString(), dataInicial, dataFinal,
					velocidadeMediaTO.getPeriodoTO().getHoraInicial(),
					velocidadeMediaTO.getPeriodoTO().getHoraFinal());
			
			List<PontoTO> pontosVeiculos = new ArrayList<PontoTO>();
			for (Map mapa : pontos) {
				PontoTO pontoTO = new PontoTO();
				pontoTO.setDataHora((String)mapa.get("DATE_TIME"));
				pontoTO.setLongitude(new Double((String)mapa.get("X")));
				pontoTO.setLatitude(new Double((String)mapa.get("Y")));	
				pontoTO.setVelocidade(new Double((String)mapa.get("VEL")));	
				pontoTO.setDistanciaUltimoPonto(new Double((String)mapa.get("DIST_BETWEEN_POSITIONS")));
				pontoTO.setDescricao((String)mapa.get("NEAR_DESC"));
				pontosVeiculos.add(pontoTO);
			}
			
			//ordena pontos do veiculo por data
			Collections.sort(pontosVeiculos, new PontosTODataComparator());

			PontoTO inicio = null;
			PontoTO fim = null;			
			
			double distanciaPercorrida = 0;
			
			for (PontoTO pontoVeiculo : pontosVeiculos) {				
								
//				descobrir ponto do ve�culo pr�ximo ao ponto inicial
				if (inicio == null && fim == null
						&& geometriaAnalitica.distanciaEntrePontos(pontoVeiculo, pontoInicial)
						<= GeometriaAnalitica.PERTO) {					
					if (pontoVeiculo.getVelocidade() > 0) {
						inicio = pontoVeiculo;
						//distanciaPercorrida += pontoVeiculo.
					}
					
//					descobrir ponto do ve�culo pr�ximo ao ponto final
				} else if (fim == null && inicio != null
						&& geometriaAnalitica.distanciaEntrePontos(pontoVeiculo, pontoFinal)
						<= GeometriaAnalitica.PERTO) {					
					if (pontoVeiculo.getVelocidade() > 0) {
						fim = pontoVeiculo;
						distanciaPercorrida += pontoVeiculo.getDistanciaUltimoPonto();
					
						//finaliza media
						velocidadeMediaTO2 = new VelocidadeMediaTO();
						velocidadeMediaTO2.setDescricaoVeiculo(veiculoTO.getDescricao());
						velocidadeMediaTO2.getPeriodoTO().setDataInicial(inicio.getDataHora());
						velocidadeMediaTO2.getPeriodoTO().setDataFinal(fim.getDataHora());
						velocidadeMediaTO2.setDistanciaPercorrida(distanciaPercorrida);
						velocidadeMediaTO2.setTempoPercurso(calcularTempoPercurso(
								velocidadeMediaTO2.getPeriodoTO().getDataInicial(), velocidadeMediaTO2.getPeriodoTO().getDataFinal()));
						velocidadeMediaTO2.setVelocidadeMediaVeiculo(calcularVelocidadeMedia(
								velocidadeMediaTO2.getDistanciaPercorrida(), velocidadeMediaTO2.getTempoPercurso()));
						velocidadesMedia.add(velocidadeMediaTO2);
						
						//zerar contadores
						inicio = null;
						fim = null;
						distanciaPercorrida = 0;
					}	
					
				} else if (inicio != null){
//					acumular valor de dist�ncia percorrida.
					if (pontoVeiculo.getVelocidade() > 0) {
						distanciaPercorrida += pontoVeiculo.getDistanciaUltimoPonto();
					}
				}
			}
			
			if (fim == null && inicio != null) {
				//finaliza media
				velocidadeMediaTO2 = new VelocidadeMediaTO();
				velocidadeMediaTO2.setDescricaoVeiculo(veiculoTO.getDescricao());
				velocidadeMediaTO2.getPeriodoTO().setDataInicial(inicio.getDataHora());
				velocidadeMediaTO2.getPeriodoTO().setDataFinal("-");				
				velocidadeMediaTO2.setVelocidadeMediaVeiculo("0");
				velocidadesMedia.add(velocidadeMediaTO2);
			}
			
			String mediaDasVelocidadesMedias = consolidarVelocidadesMedias(velocidadesMedia);
			
			Map<String, Object> dadosVeiculo = new HashMap<String, Object>();
			dadosVeiculo.put("velocidadeMediaVeiculo", velocidadeMediaTO2.getVelocidadeMediaVeiculo());
			dadosVeiculo.put("mediaConsolidada", mediaDasVelocidadesMedias);
			dadosVeiculo.put("dataInicial",velocidadeMediaTO2.getPeriodoTO().getDataInicial());
			dadosVeiculo.put("dataFinal", velocidadeMediaTO2.getPeriodoTO().getDataFinal());
			
			resultado.add(dadosVeiculo);
			
		}

		
		return resultado;
	}
	
	private String consolidarVelocidadesMedias(List<VelocidadeMediaTO> velocidades) {
				
		Double distanciaPercorrida = 0d;
		Double tempoPercorrido = 0d;
		for (VelocidadeMediaTO velocidade : velocidades) {
			try {
				distanciaPercorrida += new Double(velocidade.getDistanciaPercorrida());
			} catch (Exception e) {
				//faz nada
			}
			try {
				tempoPercorrido += new Double(velocidade.getTempoPercurso());
			} catch (Exception e) {
				//faz nada
			}			
		}

		return calcularVelocidadeMedia(distanciaPercorrida, tempoPercorrido);
	}
	
	private String calcularVelocidadeMedia(
			double distanciaPercorrida, double tempoPercurso) {
		
		//convers�o para quilometros.
		double distanciaEmKm = distanciaPercorrida / 1000;

		//calcular velocidade (km/h)
		Double divisao = distanciaEmKm/tempoPercurso; 
		if (Double.isInfinite(divisao) || Double.isNaN(divisao)) { 
			divisao = 0d;
		}
		return divisao.toString();
	}

	private double calcularTempoPercurso(String dataInicial, String dataFinal) throws ParseException {
		long entrada = DateHelper.parseDate(DateHelper.REPORT_DATE_DEFAULT, dataInicial).getTime();
		long saida = DateHelper.parseDate(DateHelper.REPORT_DATE_DEFAULT, dataFinal).getTime();
		
		double tempoPercurso = saida - entrada;
		
		int umaHora = 1000*60*60;
		
		//tempo do percur�o em horas.
		tempoPercurso = tempoPercurso/umaHora;
		return tempoPercurso;
	}
	public RotaService getRotaService() {
		return rotaService;
	}

	public void setRotaService(RotaService rotaService) {
		this.rotaService = rotaService;
	}

	public DadosLidoJdbcDAO getDadosLidoJdbcDAO() {
		return dadosLidoJdbcDAO;
	}

	public void setDadosLidoJdbcDAO(DadosLidoJdbcDAO dadosLidoJdbcDAO) {
		this.dadosLidoJdbcDAO = dadosLidoJdbcDAO;
	}	
}
